<?php
namespace Hilaoyu\LaravelExtend;

use Hilaoyu\Utils\Traits\ErrorsTrait;
use Hilaoyu\Utils\UtilHtml;
use Hilaoyu\Utils\UtilSupport;
use Illuminate\Contracts\Auth\Access\Gate;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Collection;
use Hilaoyu\LaravelExtend\Supports\IlluminateQuery;

/**
 * Created by laoyu.
 * User: hilaoyu@qq.com
 * Date: 2018/2/25
 * Time: 17:16
 */
abstract class Entity
{
    use ErrorsTrait;

    abstract static protected function classNameModelMain() : string ;

    /**
     * @var array|string[]
     */
    public static array $find_by_id_columns = ['*'];
    public function __construct()
    {
    }


    public function modelclassName(){
        return static::classNameModelMain();
    }
    /**
     * @param string $id
     * @param bool $new_if_not_exists
     * @return Model
     */
    public function model($id='',$new_if_not_exists=false){
        $model =  UtilSupport::makeClass(static::classNameModelMain());
        if($model && $id){
            $model = $model::makeModel($id,$new_if_not_exists);
        }
        return $model;
    }

    /**
     * @param array $where
     * @param array $sort
     * @param int $offset
     * @param int $start
     * @param null $total
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public function select($where=[],$sort=[],$offset=0,$start=0,&$total=null){

        return $this->modelSearch($this->model(),$where,$sort,$offset,$start,$total);
    }

    /**
     * @param array $where
     * @return Model
     */
    public function find($where=[]){

        return $this->modelFind($this->model(),$where);
    }

    /**
     * @param $id
     * @return Model
     */
    public function findById($id){
        $model = $this->model();
        if($id instanceof $model){
            $id = $id->getKey();
        }
        $item  =   $this->find([$model->getKeyName()=>$id]);
        unset($model);
        return $item;
    }


    /**
     * @param $data
     * @param string $id
     * @return Model|null
     * @throws \Exception
     */
    public function save($data,$id=''){
        if( ! ($model =  UtilSupport::makeClassSingle(static::classNameModelMain())::makeModel($id))){
            $this->SetError('model data not exits');
            return null;
        }

        if(! ($model = $this->alterSaveFillDataBefore($model,$data))){
            return null;
        }



        $model->fill($data);


        $DB = $model->getConnection();
        $DB->beginTransaction();

        try{
            if(!($model = $this->alterSaveBefore($model,$data))){
                throw new \Exception();
            }
            $Ex = $model->save();
            
            if(!$Ex){
                $this->SetError($model->getError());
                throw new \Exception();
            }

            $model = $this->alterSaveAfter($model,$data,$Ex);

            if(!$model || !$Ex){
                throw new \Exception();
            }


            $DB->commit();

            $model = $this->alterSaveCommitAfter($model,$data);



        }catch (\Exception $exception){
            if($message = $exception->getMessage()){
                $this->SetError($message);
            }

            $DB->rollBack();
            return null;
        }


        return $model;


    }

    /**
     * @param $model
     * @param $data
     * @return Model
     */
    protected function alterSaveFillDataBefore($model,&$data){
        return $model;
    }

    /**
     * @param $model
     * @param $data
     * @return Model
     */
    protected function alterSaveBefore($model,&$data){
        return $model;
    }

    /**
     * @param $model
     * @param $data
     * @param $Ex
     * @return Model
     */
    protected function alterSaveAfter($model,$data,&$Ex){
        return $Ex ? $model : null;
    }
    protected function alterSaveCommitAfter($model,$data){
        return $model;
    }



    /**
     * @param array $where
     * @return bool
     */
    public function deleteMulti($where=[],$force=false) {
        if(empty($where)){
            $this->SetError('$where not be empty');
            return false;
        }

        $models = $this->select($where);

        $flag = true;

        $models->each(function ($model)use (&$flag,$force){
            $flag = $flag && $this->delete($model,$force);
            return $flag;
        });

        return $flag;
    }


    /**
     * @param $id
     * @return bool
     */
    public function delete($id,$force=false)
    {
        if(empty($id) ||  ! ($model =  UtilSupport::makeClassSingle(static::classNameModelMain())::makeModel($id))){
            $this->SetError('model data not exits');
            return true;
        }

        $model = $this->alterDeleteBeforeTrans($model,$force);
        if (true === $model){
            return true;
        }
        if(!$model){
            return false;
        }

        $flag = true;
        $DB = $model->getConnection();
        $DB->beginTransaction();

        try{

            $model = $this->alterDeleteBefore($model,$force);

            if(!$model){
                throw new \Exception('');
            }

            $flag = UtilSupport::tryQuiet(function () use (&$model,$force){
                if($force){
                    return $model->forceDelete();
                }
                return $model->delete();
            });

            if(!$flag){
                $this->SetError($model->getError());
                throw new \Exception('');
            }

            $this->alterDeleteAfter($model,$flag,$force);

            if(!$flag){
                throw new \Exception('');
            }

            $DB->commit();

        }catch (\Exception $exception){
            if($message = $exception->getMessage()){
                $this->SetError($message);
            }

            $DB->rollBack();

            $flag = false;
        }


        return $flag;
    }


    /**
     * @param $model
     * @return Model
     */
    protected function alterDeleteBeforeTrans($model,$force=false){
        return $model;
    }

    /**
     * @param $model
     * @return Model
     */
    protected function alterDeleteBefore($model,$force=false){
        return $model;
    }


    /**
     * @param $model
     * @param $Ex
     * @return Model
     */
    protected function alterDeleteAfter($model,&$Ex,$force=false){
        return $model;
    }

    /**
     * 内容搜索列表
     * @param $model 数据表模型
     * @param array $where 数据组 或 闭包函数
     * @param array $sort 数组 [field=>'asc|desc',field=>'asc|desc']
     * @param int $offset 分页条数  大于0时才生效
     * @param int $start 起始位置
     * @param null $total 总数，引用传递，传入不为null的仠是生效
     * @return Collection
     */

    public function modelSearch($model,$where=[],$sort=[],$offset=0,$start=0,&$total=null){


        $withCount = false;
        if(!is_null($total)){
            $withCount = true;
            $total = 0;
        }
        if(!($model = IlluminateQuery::buildWhere($model,$where))){
            return new Collection();
        }


        $model = $this->modelQueryAlter($model);

        if($withCount){

            $total = $model->count();
        }

        if(!empty($sort)){
            foreach ($sort as $column=>$direction){

                $model = $model->orderBy($column,$direction);
            }
        }


        $start = intval($start);
        if($start < 0){
            $start = 0;
        }


        $offset = intval($offset);
        if($offset > 0){
            $model = $model->skip($start);
            $model = $model->take($offset);
        }


        try{
            $results = $model->get();
        }catch (\Exception $e){
            $this->setError($e->getMessage());
            return new Collection();

        }


        if(false === $results){
            $this->setError($model->getError);
        }

        return $results;
    }

    /**
     * 内容搜索第一条
     * @param $model 数据表模型
     * @param array $where 数据组 或 闭包函数
     * @return Model
     */

    public function modelFind($model,$where=[]){

        if(!($model = IlluminateQuery::buildWhere($model,$where))){
            return false;
        }

        $model = $this->modelQueryAlter($model);

        $result = $model->first(self::$find_by_id_columns);
        if(false === $result){
            $this->setError($model->getError);
        }

        return $result;
    }

    /**
     * 删除
     * @param $model 数据表模型
     * @param array $where 数据组 或 闭包函数
     * @return bool
     */
    public function modelDelete($model,$where=[]) {

        if(!($model = IlluminateQuery::buildWhere($model,$where))){
            return false;
        }

        $model = $this->modelQueryAlter($model);

        $result = $model->delete();
        if(false === $result){
            $this->setError($model->getError);
        }

        return $result;
    }

    /**
     * @param $query
     * @return Builder | \Illuminate\Database\Eloquent\Builder
     */
    protected function modelQueryAlter($query) {
        return $query;
    }



    protected function authorize($ability, $arguments = []){
        return app(Gate::class)->authorize($ability, $arguments);
    }


    public function buildSelectOptions($where=[],$selected = '', $add_title = '',array $sort=[],string $label_key='',array $load_relations=[])
    {

        $add_title = trim($add_title);
        $label_key = trim($label_key);
        $options = [];
        if ($add_title) {
            $options[] = ['value' => '', 'label' => $add_title];
        }

        $models = $this->select($where,$sort);
        if($models && !empty($load_relations)){
            $models->load($load_relations);
        }
        if(!$label_key){
            $label_key = 'name';
        }
        $models->each(function ($item) use (&$options,$label_key){
            $label = data_get($item,$label_key,'');
            if($status_name = data_get($item,'status_name')){
                $label .= '('. $status_name .')';
            }
            $options[] = ['value' => $item->id, 'label' => $label];
            unset($label,$status_name);
        });

        if (empty($options)) {
            $options[] = ['value' => '_non_', 'label' => '无数据'];
        }

        return UtilHtml::formatHtmlOptions($options, 'value', 'label', '', $selected);
    }


    public function fileUploaders(){
        return UtilSupport::tryQuiet(function (){
            return $this->model()->fileUploaders();
        });
    }

    /**
     *
     *
     * @param array $columns
     * @return $this
     * @author Iwtrgw 2024/8/29 14:09
     */
    public function setFindByIdColumns(array $columns = ['*']): static
    {
        self::$find_by_id_columns = $columns;
        return $this;
    }

}
